home *** CD-ROM | disk | FTP | other *** search
/ Eagles Nest BBS 8 / Eagles_Nest_Mac_Collection_Disc_8.TOAST / Developer Tools⁄Additions / MacScheme20 / Compatibility / hashtables.sch < prev    next >
Encoding:
Text File  |  1988-12-23  |  13.8 KB  |  349 lines  |  [TEXT/EDIT]

  1. ; Hash tables for MacScheme.
  2. ;
  3. ; (sxhash x)
  4. ;   returns a fixnum hash code for x with the property that
  5. ;   (equal? x y) implies (= (sxhash x) (sxhash y)).
  6. ;
  7. ; (make-eq?-hashtable)
  8. ; (make-eqv?-hashtable)
  9. ; (make-equal?-hashtable)
  10. ;
  11. ; These three procedures return a hash table h with the
  12. ; following operations:
  13. ;
  14. ; (h 'population)
  15. ;    returns the number of items in the hash table.
  16. ; ((h 'rehash) newsize)
  17. ;    rehashes the table to a size that can accomodate at least
  18. ;    newsize items.  Rehashing occurs automatically as needed.
  19. ; ((h 'lookup) x)
  20. ;    returns the entry for x, or #f if there is none.
  21. ; ((h 'change) x entry)
  22. ;    changes the entry for x to be entry, signalling an error
  23. ;    if x is not already in the table.
  24. ; ((h 'add) x entry)
  25. ;    changes the entry for x to be entry, creating a new item
  26. ;    if x is not already in the table.
  27. ; ((h 'remove) x)
  28. ;    removes x from the table.
  29. ; (h 'clear)
  30. ;    clears all items from the table.
  31. ; ((h 'for-each) f)
  32. ;    calls f once for every item in the table.  The two arguments
  33. ;    to f are the key and the entry associated with that key.
  34. ;
  35. ; The three kinds of hash table objects correspond roughly to
  36. ; association lists searched using assq, assv, and assoc.
  37.  
  38. ; These hash tables are not blindingly fast.  Association lists
  39. ; are faster for tables with fewer than 100 or so entries.
  40.  
  41. ; Implementation note.  Hash-on-eq? is implemented by using the
  42. ; pointer part of a object as the hash code.  Since MacScheme
  43. ; uses a relocating garbage collector, the hash table must be
  44. ; rehashed following a garbage collection.  This implementation
  45. ; delays the rehashing as long as possible instead of rehashing
  46. ; after every garbage collection.
  47. ;
  48. ; Most operations must check that the garbage collector has not
  49. ; run since the table was last rehashed, and the garbage collector
  50. ; must not run between this check and the end of the operation.
  51. ; This means that the critical part of each operation cannot
  52. ; allocate any storage.  Such code cannot be written without
  53. ; detailed knowledge of the compiler and run-time system.  For
  54. ; example, MacScheme's interrupt system allocates storage so
  55. ; most hash table operations have to run with interrupts disabled.
  56.  
  57. ; This definition of call-without-interrupts was extracted from
  58. ; Library:Basic:general.sch.
  59.  
  60. (define (call-without-interrupts thunk)
  61.   (let ((mask (syscall (vector -23 1)))
  62.         (ans 0))
  63.     (syscall (vector -23 (logior mask 1)))
  64.     (set! ans (thunk))
  65.     (syscall (vector -23 mask))
  66.     ans))
  67.  
  68. (define (make-eq?-hashtable)
  69.   (let ((tablesize 64)
  70.         (unused (list #f))
  71.         (deleted (list #f)))
  72.     (let ((keys (make-vector tablesize unused))
  73.           (entries (make-vector tablesize #f))
  74.           (population 0)
  75.           (gcinfo (vector -1 0 0 0))
  76.           (gccount 0))
  77.       (define (rehash-if-necessary)
  78.         (if (not (valid?))
  79.             (rehash tablesize)))
  80.       (define (valid?)
  81.         (syscall gcinfo)
  82.         (= (vector-ref gcinfo 1) gccount))
  83.       (define (hash x)
  84.         (remainder (+ (typetag x) (typetag-set! x 0)) tablesize))
  85.       (define (search x i)
  86.         (let ((y (vector-ref keys i)))
  87.           (cond ((eq? y x) i)
  88.                 ((eq? y unused) #f)
  89.                 (else (searchloop x (+ i 1) i)))))
  90.       (define (searchloop x i stop)
  91.         (cond ((= i tablesize) (searchloop x 0 stop))
  92.               ((= i stop) #f)
  93.               (else (let ((y (vector-ref keys i)))
  94.                       (cond ((eq? y x) i)
  95.                             ((eq? y unused) #f)
  96.                             (else (searchloop x (+ i 1) stop)))))))
  97.       (define (nextavail i)
  98.         (let ((y (vector-ref keys i)))
  99.           (cond ((eq? y unused) i)
  100.                 ((eq? y deleted) i)
  101.                 (else (nextavailloop (+ i 1) i)))))
  102.       (define (nextavailloop i stop)
  103.         (cond ((= i tablesize) (nextavailloop 0 stop))
  104.               ((= i stop) ???)
  105.               (else (let ((y (vector-ref keys i)))
  106.                       (cond ((eq? y unused) i)
  107.                             ((eq? y deleted) i)
  108.                             (else (nextavailloop (+ i 1) stop)))))))
  109.       (define (rehash newsize)
  110.         (call-without-interrupts
  111.          (lambda ()
  112.            (if (< newsize population)
  113.                (rehash population)
  114.                (let ((oldkeys keys)
  115.                      (oldentries entries)
  116.                      (oldsize tablesize))
  117.                  (set! keys (make-vector newsize unused))
  118.                  (set! entries (make-vector newsize #f))
  119.                  (set! tablesize newsize)
  120.                  (syscall gcinfo)
  121.                  (set! gccount (vector-ref gcinfo 1))
  122.                  (do ((i (- oldsize 1) (- i 1)))
  123.                      ((< i 0) #t)
  124.                      (let ((x (vector-ref oldkeys i)))
  125.                        (cond ((eq? x unused) #f)
  126.                              ((eq? x deleted) #f)
  127.                              (else (let ((j (nextavail (hash x))))
  128.                                      (vector-set! keys j x)
  129.                                      (vector-set! entries
  130.                                                   j
  131.                                                   (vector-ref oldentries i))))))))))))
  132.       (define (lookup x)
  133.         (call-without-interrupts
  134.          (lambda ()
  135.            (rehash-if-necessary)
  136.            (let ((i (search x (hash x))))
  137.              (if i
  138.                  (vector-ref entries i)
  139.                  #f)))))
  140.       (define (change x entry)
  141.         (call-without-interrupts
  142.          (lambda ()
  143.            (rehash-if-necessary)
  144.            (let ((i (search x (hash x))))
  145.              (if i
  146.                  (begin (vector-set! entries i entry) #t)
  147.                  (error "Hashtable entry not found" x))))))
  148.       (define (add x entry)
  149.         (call-without-interrupts
  150.          (lambda ()
  151.            (rehash-if-necessary)
  152.            (let ((i (search x (hash x))))
  153.              (if i
  154.                  (begin (vector-set! entries i entry) #t)
  155.                  (begin
  156.                   (if (>= (quotient (* 10 population) tablesize) 9)
  157.                       (rehash (* 2 tablesize)))
  158.                   (let ((i (nextavail (hash x))))
  159.                     (vector-set! keys i x)
  160.                     (vector-set! entries i entry)
  161.                     (set! population (+ population 1))
  162.                     #t)))))))
  163.       (define (rem x)
  164.         (call-without-interrupts
  165.          (lambda ()
  166.            (rehash-if-necessary)
  167.            (let ((i (search x (hash x))))
  168.              (if i
  169.                  (begin (vector-set! keys i deleted)
  170.                         (vector-set! entries i #f)
  171.                         (set! population (- population 1))
  172.                         #t)
  173.                  #f)))))
  174.       (define (clear)
  175.         (call-without-interrupts
  176.          (lambda ()
  177.            (do ((i (- tablesize 1) (- i 1)))
  178.                ((< i 0) (set! population 0) #t)
  179.                (vector-set! keys i unused)
  180.                (vector-set! entries i #f)))))
  181.       (define (foreach f)
  182.         (apply for-each
  183.                (cons f (call-without-interrupts keys&entries))))
  184.       (define (keys&entries)
  185.         (keys&entries-loop (- tablesize 1) '() '()))
  186.       (define (keys&entries-loop i l1 l2)
  187.         (if (< i 0)
  188.             (list l1 l2)
  189.             (let ((y (vector-ref keys i)))
  190.               (cond ((eq? y unused) (keys&entries-loop (- i 1) l1 l2))
  191.                     ((eq? y deleted) (keys&entries-loop (- i 1) l1 l2))
  192.                     (else (keys&entries-loop (- i 1)
  193.                                              (cons y l1)
  194.                                              (cons (vector-ref entries i) l2)))))))
  195.       (syscall gcinfo)
  196.       (set! gccount (vector-ref gcinfo 1))
  197.       (%object self
  198.                ((population) population)
  199.                ((rehash newsize) (rehash newsize))
  200.                ((lookup x) (lookup x))
  201.                ((change x entry) (change x entry))
  202.                ((add x entry) (add x entry))
  203.                ((remove x) (rem x))
  204.                ((clear) (clear))
  205.                ((for-each f) (foreach f))))))
  206.  
  207. ; hash-on-eqv? tables are implemented using a hash-on-equal? table
  208. ; for numbers and a hash-on-eq? table for everything else.
  209.  
  210. (define (make-eqv?-hashtable)
  211.   (let ((h1 (make-eq?-hashtable))
  212.         (h2 (make-equal?-hashtable)))
  213.     (%object self
  214.              ((population)
  215.               (+ (h1 'population) (h2 'population)))
  216.              ((rehash newsize)
  217.               ((h1 'rehash) newsize) ((h2 'rehash) newsize))
  218.              ((lookup x)
  219.               (((if (number? x) h2 h1) 'lookup) x))
  220.              ((change x entry)
  221.               (((if (number? x) h2 h1) 'change) x entry))
  222.              ((add x entry)
  223.               (((if (number? x) h2 h1) 'add) x entry))
  224.              ((remove x)
  225.               (((if (number? x) h2 h1) 'remove) x))
  226.              ((clear)
  227.               (h1 'clear) (h2 'clear))
  228.              ((for-each f)
  229.               ((h1 'for-each) f)
  230.               ((h2 'for-each) f)))))
  231.  
  232. ; A hash-on-equal? table is implemented using sxhash and a hash-on-eq?
  233. ; table whose entries are association lists.
  234.  
  235. (define (make-equal?-hashtable)
  236.   (let ((h (make-eq?-hashtable))
  237.         (population 0))
  238.     (define (change-error x)
  239.       (error "Hashtable entry not found" x))
  240.     (define (lookup x)
  241.       (let ((item (assoc x ((h 'lookup) (sxhash x)))))
  242.         (if item
  243.             (cdr item)
  244.             #f)))
  245.     (define (change x entry)
  246.       (call-without-interrupts
  247.        (lambda ()
  248.          (let ((bucket ((h 'lookup) (sxhash x))))
  249.            (if bucket
  250.                (let ((item (assoc x bucket)))
  251.                  (if item
  252.                      (begin (set-cdr! item entry) #t)
  253.                      (change-error x)))
  254.                (change-error x))))))
  255.     (define (add x entry)
  256.       (call-without-interrupts
  257.        (lambda ()
  258.          (let ((k (sxhash x)))
  259.            (let ((bucket ((h 'lookup) k)))
  260.              (if bucket
  261.                  (let ((item (assoc x bucket)))
  262.                    (if item
  263.                        (begin (set-cdr! item entry) #t)
  264.                        (begin ((h 'change) k (cons (cons x entry) bucket))
  265.                               (set! population (+ population 1))
  266.                               #t)))
  267.                  ((h 'add) k (list (cons x entry)))))))))
  268.     (define (rem x)
  269.       (call-without-interrupts
  270.        (lambda ()
  271.          (let ((k (sxhash x)))
  272.            (let ((bucket ((h 'lookup) k)))
  273.              (if bucket
  274.                  (let ((item (assoc x bucket)))
  275.                    (if item
  276.                        (begin ((h 'change) k (remove item bucket))
  277.                               (set! population (- population 1))
  278.                               #t)
  279.                        #f))
  280.                  #f))))))
  281.     (define (foreach f)
  282.       ((h 'for-each)
  283.        (lambda (ignore bucket)
  284.          (for-each f (map car bucket) (map cdr bucket)))))
  285.     (%object self
  286.              ((population)     population)
  287.              ((rehash newsize) ((h 'rehash) newsize))
  288.              ((lookup x)       (lookup x))
  289.              ((change x entry) (change x entry))
  290.              ((add x entry)    (add x entry))
  291.              ((remove x)       (rem x))
  292.              ((clear)          (h 'clear) (set! population 0) #t)
  293.              ((for-each f)     (foreach f)))))
  294.  
  295. (define sxhash
  296.   (letrec ((sxhash
  297.             (lambda (x n)
  298.               (let ((tt (typetag x)))
  299.                 (cond ((<= n 0) (lsh tt 14))
  300.                       ((fixnum? x) (logand mask (+ mask x)))
  301.                       ((pair? x)
  302.                        (logand mask
  303.                                (+ tt
  304.                                   (remainder (* (sxhash (car x) (- n 1))
  305.                                                 (- (sxhash (cdr x) (- n 1)) 1))
  306.                                              mask))))
  307.                       ((symbol? x)
  308.                        (logand mask (+ 10000 (sxhash (symbol->string x) n))))
  309.                       ((procedure? x)
  310.                        (logand mask (+ 10001 (sxhash (->pair x) n))))
  311.                       ((vector? x)
  312.                        (let ((m (vector-length x)))
  313.                          (logand mask
  314.                                  (+ tt
  315.                                     m
  316.                                     (if (> m 0)
  317.                                         (+ (sxhash (vector-ref x 0) (- n 1))
  318.                                            (sxhash (vector-ref x (quotient m 2)) (- n 1))
  319.                                            (sxhash (vector-ref x (- m 1)) (- n 1)))
  320.                                         0)))))
  321.                       ((bytevector? x)
  322.                        (let ((m (bytevector-length x)))
  323.                          (do ((h (+ tt m) (logand mask (+ h (lsh (bytevector-ref x i) j))))
  324.                               (i (min 16 (- m 1)) (- i 1))
  325.                               (j 0 (+ j 1)))
  326.                              ((< i 0) h))))
  327.                       ((string? x)
  328.                        (let ((m (string-length x)))
  329.                          (logand mask
  330.                                  (+ tt
  331.                                     m
  332.                                     (if (> m 0) (char->integer (string-ref x 0)) -3253)
  333.                                     (if (> m 1) (lsh (char->integer (string-ref x 1)) 8) 333)
  334.                                     (if (> m 2) (lsh (char->integer (string-ref x 2)) 16) 135079)
  335.                                     (if (> m 3) (char->integer (string-ref x 3)) -28301)
  336.                                     (if (> m 4) (lsh (char->integer (string-ref x 4)) 8) 947)))))
  337.                       ((< tt #x40)
  338.                        (+ (lsh tt 17) (logand mask (typetag-set! x 0))))
  339.                       ((< tt #x50)
  340.                        (+ (- tt #x50) (sxhash (typetag-set! x #x40) n)))
  341.                       ((< tt #x60)
  342.                        (+ (- tt #x60) (sxhash (typetag-set! x #x50) n)))
  343.                       ((< tt #x70)
  344.                        (+ (- tt #x70) (sxhash (typetag-set! x #x60) n)))
  345.                       (else (+ 1000000 tt))))))
  346.            (mask (- (expt 2 24) 1)))
  347.     (lambda (x) (sxhash x 5))))
  348.  
  349.